package com.yiba.vpn.authorization.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.yiba.vpn.authorization.annotation.Authorization;
import com.yiba.vpn.authorization.manager.impl.RedisTokenManager;
import com.yiba.vpn.authorization.model.CheckTokenModel;
import com.yiba.vpn.authorization.manager.TokenManager;
import com.yiba.vpn.config.Config;
import com.yiba.vpn.config.Constants;
import com.yiba.vpn.config.ResultStatus;
import com.yiba.vpn.util.StringUtils;
import com.yiba.vpn.util.TokenUtil;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * 自定义拦截器，判断此次请求是否有权限
 *
 * @author ScienJus
 * @date 2015/7/30.
 * @see com.yiba.vpn.authorization.annotation.Authorization
 */

@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {


    private static Logger logger = Logger.getLogger(AuthorizationInterceptor.class);

    @Autowired
    private TokenManager manager;

    @Autowired
    private HttpServletRequest request;


    @Autowired
    private Config config;

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        //从header中得到token
        String authorization = request.getHeader(Constants.AUTHORIZATION);

        //解密token
        CheckTokenModel model = decryptToken(authorization);

        //校验token 状态
        int code = validateToken(model);
        boolean flag;
        if (code == 401) {
            getResult(ResultStatus.TOKEN_ERROR.getCode(), ResultStatus.TOKEN_ERROR.getMessage(), response);
            flag = false;
        } else if (code == 402) {
            getResult(ResultStatus.OTHER_DEVICE_LOGION.getCode(), ResultStatus.OTHER_DEVICE_LOGION.getMessage(), response);
            flag = false;
        } else if (code == 200) {
            //如果token验证成功，将token对应的用户id存在request中，便于之后注入
            request.setAttribute(Constants.CURRENT_USER_ID, model.getAccount());
            flag = true;
        } else {
            getResult(ResultStatus.TOKEN_ERROR.getCode(), ResultStatus.TOKEN_ERROR.getMessage(), response);
            flag = false;
        }

        //如果验证token失败，并且方法注明了Authorization，返回401错误
        if (method.getAnnotation(Authorization.class) != null && code != 200) {
            getResult(ResultStatus.TOKEN_ERROR.getCode(), ResultStatus.TOKEN_ERROR.getMessage(), response);
            flag = false;
        }
        return flag;
    }

    private void getResult(int code, String msg, HttpServletResponse response) throws IOException {
        response.setCharacterEncoding(Constants.CHARACTER_ENCODING);
        response.setContentType(Constants.CONTENT_TYPE);
        response.setStatus(HttpServletResponse.SC_OK);
        JSONObject res = new JSONObject();
        res.put(Constants.CODE_STR, code);
        res.put(Constants.MSG_STR, msg);
        res.put(Constants.DATA_STR, new HashMap<>());
        PrintWriter out = response.getWriter();
        out.append(res.toString());
    }

    /**
     * 解密token
     *
     * @param authentication
     * @return
     */
    private CheckTokenModel decryptToken(String authentication) {

        CheckTokenModel checkTokenModel = null;
        if (StringUtils.isEmpty(authentication)) {
            return checkTokenModel;
        }

        try {
            String decryptToken = TokenUtil.decrypt(authentication, config.getTokenKey());
            String[] param = decryptToken.split(TokenUtil.SPLIT);

            if (param.length != 3) {
                return checkTokenModel;
            }
            String account = param[0];
            String uuid = param[1];
            checkTokenModel = new CheckTokenModel(account, uuid, authentication);
        } catch (Exception e) {
            logger.error("token解密系统异常:{}" + e);
            return checkTokenModel;
        }
        return checkTokenModel;

    }

    /**
     * 校验token
     *
     * @param model
     * @return
     */
    public int validateToken(CheckTokenModel model) {
        int flag;
        if (model == null) {
            flag = 401;
            return flag;
        }

        String exitsToken = manager.getValue(model.getAccount());
        if (StringUtils.isEmpty(exitsToken)) {
            flag = 401;
            return flag;
        }

        //token 不为空
        if (exitsToken.equals(model.getToken())) {
            //如果token一样
            flag = 200;
            //如果验证成功，说明此用户进行了一次有效操作，延长token的过期时间
            manager.expireValue(model.getAccount(), Constants.TOKEN_EXPIRES_SECONDS, TimeUnit.SECONDS);
        } else {
            //token 不一样 比较UUID是不是同一个设备登录的,网页版本的uuid不计入登录比较
            CheckTokenModel existToken = decryptToken(exitsToken);
            if (existToken.getUuid().equals(model.getUuid())) {
                flag = 401;
            } else {
                flag = 402;
            }
        }
        return flag;
    }
}
